package cz.data.domain.metadata.service.impl;

import cz.data.commo.office.word.WordUtil;
import cz.data.common.base.BaseResponse;
import cz.data.common.mybatis.BaseServiceImpl;
import cz.data.common.core.DataConstant;
import cz.data.common.redis.config.CacheNames;
import cz.data.common.support.db.DbProperty;
import cz.data.common.support.db.core.DbColumn;
import cz.data.common.support.db.core.DbTable;
import cz.data.common.exception.DataException;
import cz.data.common.utils.SecurityUtil;
import cz.data.domain.driver.rpc.IDriverFeign;
import cz.data.domain.metadata.model.MetadataUtil;
import cz.data.domain.metadata.service.RemoteDriverService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import com.aspose.words.Document;
import com.aspose.words.MailMerge;
import com.aspose.words.net.System.Data.DataRelation;
import com.aspose.words.net.System.Data.DataRow;
import com.aspose.words.net.System.Data.DataSet;
import com.aspose.words.net.System.Data.DataTable;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import cz.data.common.utils.SpringContextHolder;
import cz.data.domain.metadata.dao.MetadataColumnDao;
import cz.data.domain.metadata.dao.MetadataSourceDao;
import cz.data.domain.metadata.dao.MetadataTableDao;
import cz.data.domain.metadata.listener.event.MetadataExploreEvent;
import cz.data.domain.metadata.listener.event.MetadataSourceChangeEvent;
import cz.data.domain.metadata.listener.event.MetadataSourceDeleteEvent;
import cz.data.domain.metadata.mapstruct.MetadataSourceMapper;
import cz.data.domain.metadata.model.dto.DbSchema;
import cz.data.domain.metadata.model.dto.MetadataSourceDto;
import cz.data.domain.metadata.model.entity.MetadataAuthorizeEntity;
import cz.data.domain.metadata.model.entity.MetadataColumnEntity;
import cz.data.domain.metadata.model.entity.MetadataSourceEntity;
import cz.data.domain.metadata.model.entity.MetadataTableEntity;
import cz.data.domain.metadata.model.enums.DataLevel;
import cz.data.domain.metadata.model.enums.SyncStatus;
import cz.data.domain.metadata.service.MetadataAuthorizeService;
import cz.data.domain.metadata.service.MetadataSourceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
@Service
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class MetadataSourceServiceImpl extends BaseServiceImpl<MetadataSourceDao, MetadataSourceEntity> implements MetadataSourceService {

    @Autowired
    private MetadataSourceDao metadataSourceDao;

    @Autowired
    private MetadataSourceMapper metadataSourceMapper;

    @Autowired
    private MetadataTableDao metadataTableDao;

    @Autowired
    private MetadataColumnDao metadataColumnDao;

    @Autowired
    MetadataAuthorizeService metadataAuthorizeService;
    @Resource
    ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Resource
    RemoteDriverService remoteDriverService;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveMetadataSource(MetadataSourceDto metadataSourceDto) {
        MetadataSourceEntity dataSource = metadataSourceMapper.toEntity(metadataSourceDto);
        dataSource.setIsSync(SyncStatus.NotSync.getKey());
        metadataSourceDao.insert(dataSource);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @Cacheable(cacheNames = CacheNames.CACHE_1HOUSE, key = "'metadata:source:' + #root.args[0].id")
    public void updateMetadataSource(MetadataSourceDto metadataSourceDto) {
        MetadataSourceEntity dataSource = metadataSourceMapper.toEntity(metadataSourceDto);
        int update = metadataSourceDao.updateById(dataSource);
        if (update > 0) {
            SpringContextHolder.publishEvent(new MetadataSourceChangeEvent(dataSource.getId()));
        }
    }

    @Override
    @Cacheable(cacheNames = CacheNames.CACHE_1HOUSE, key = "'metadata:source:' + #id", unless = "#result == null")
    public MetadataSourceEntity getMetadataSourceById(String id) {
        return super.getById(id);
    }

    @Override
    public List<MetadataSourceEntity> getMetadataSourceList() {
        boolean admin = SecurityUtil.isAdmin();
        List<MetadataSourceEntity> sourceList = list();
        Stream<MetadataSourceEntity> stream = Optional.ofNullable(sourceList).orElseGet(ArrayList::new).stream()
                .filter(s -> DataConstant.EnableState.ENABLE.getKey().equals(s.getStatus()));
        if (!admin) {
            Set<String> set = new HashSet<>();
            List<String> roleIds = SecurityUtil.getUserRoleIds();
            roleIds.forEach(role -> {
                List<MetadataAuthorizeEntity> list = metadataAuthorizeService.getMetadataAuthorizeByRoleId(role);
                set.addAll(Optional.ofNullable(list).orElseGet(ArrayList::new).stream()
                        .filter(s -> Objects.equals(DataLevel.DATABASE.getKey(), s.getObjectType()))
                        .map(MetadataAuthorizeEntity::getObjectId).collect(Collectors.toSet()));
            });
            stream = stream.filter(s -> set.contains(s.getId()));
        }
        return stream.collect(Collectors.toList());
    }

    @Override
    public List<MetadataSourceEntity> getMetadataSourceSelectOptions(List<String> capability, String dbType) {
        return metadataSourceDao.getMetadataSourceSelectOptions(capability, dbType);
    }

    @Override
    public <E extends IPage<MetadataSourceEntity>> E pageWithAuth(E page, Wrapper<MetadataSourceEntity> queryWrapper) {
        boolean admin = SecurityUtil.isAdmin();
        List<String> roles = new ArrayList<>();
        if (!admin) {
            roles = SecurityUtil.getUserRoleIds();
        }
        return metadataSourceDao.selectPageWithAuth(page, queryWrapper, roles);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteMetadataSourceById(String id) {
        metadataSourceDao.deleteById(id);
        //级联删除表和列
        SpringContextHolder.publishEvent(new MetadataSourceDeleteEvent(id));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteMetadataSourceBatch(List<String> ids) {
        metadataSourceDao.deleteBatchIds(ids);
    }

    @Override
    public BaseResponse checkConnection(MetadataSourceDto metadataSourceDto) {
        MetadataSourceEntity dataSource = metadataSourceMapper.toEntity(metadataSourceDto);
        DbProperty property = MetadataUtil.dbProperty(dataSource);
        //使用一次性数据源
        try {
            IDriverFeign driverFeign = remoteDriverService.getDriverFeign(property.getDbType());
            BaseResponse response = driverFeign.connect(property);
            if (response.getSuccess()) {
                return BaseResponse.success(200, "连接成功");
            } else {
                return BaseResponse.error(500, response.getMsg());
            }
        } catch (Exception e) {
            log.warn("测试数据源连接错误: {}", e.getMessage());
            return BaseResponse.error(500, "数据库连接有误,请检查数据库配置是否正确");
        }
    }

    @Override
    public List<DbTable> getDbTables(String id) {
        MetadataSourceEntity dataSource = super.getById(id);
        IDriverFeign driverFeign = remoteDriverService.getDriverFeign(dataSource.getDbType());
        return driverFeign.getSourceAllTable(dataSource.getId());
    }

    @Override
    public List<DbColumn> getDbTableColumns(String id, String tableName) {
        MetadataSourceEntity dataSource = super.getById(id);
        IDriverFeign driverFeign = remoteDriverService.getDriverFeign(dataSource.getDbType());
        return driverFeign.getSourceTableColumns(dataSource.getId(), tableName);
    }

    @Override
    public void syncMetadata(String id) {
        MetadataSourceEntity metadataSourceEntity = super.getById(id);
        if (SyncStatus.InSync.getKey().equals(metadataSourceEntity.getIsSync())) {
            throw new DataException("元数据同步中");
        }
        metadataSourceEntity.setIsSync(SyncStatus.InSync.getKey());
        metadataSourceDao.updateById(metadataSourceEntity);
        // 异步执行同步任务
        CompletableFuture.runAsync(
                () -> SpringContextHolder.publishEvent(new MetadataExploreEvent(metadataSourceEntity)),
                threadPoolTaskExecutor
        ).whenCompleteAsync((response, e) -> {
            if (Objects.isNull(e)) {
                metadataSourceEntity.setIsSync(SyncStatus.IsSync.getKey());
            } else {
                log.error(e.getMessage(), e);
                metadataSourceEntity.setIsSync(SyncStatus.NotSync.getKey());
            }
            metadataSourceDao.updateById(metadataSourceEntity);
        });
    }

    @Override
    public Document wordMetadata(String id) throws Exception {
        MetadataSourceEntity metadataSourceEntity = super.getById(id);
        DbSchema dbSchema = metadataSourceEntity.getDbSchema();
        String dbName = dbSchema.getDbName();
        if (StringUtils.isBlank(dbName)) {
            dbName = dbSchema.getUsername();
        }
        QueryWrapper<MetadataTableEntity> tableQueryWrapper = new QueryWrapper<>();
        tableQueryWrapper.eq("source_id", id);
        List<MetadataTableEntity> tableEntityList = metadataTableDao.selectList(tableQueryWrapper);
        // 数据表（主表） TableStart:TableList TableEnd:TableList
        DataTable tableTable = new DataTable("TableList");
        tableTable.getColumns().add("id");
        tableTable.getColumns().add("tableName");
        tableTable.getColumns().add("tableComment");
        for (MetadataTableEntity table : tableEntityList) {
            DataRow row = tableTable.newRow();
            row.set(0, table.getId());
            row.set(1, table.getTableName());
            row.set(2, table.getTableComment());
            tableTable.getRows().add(row);
        }
        QueryWrapper<MetadataColumnEntity> columnQueryWrapper = new QueryWrapper<>();
        columnQueryWrapper.eq("source_id", id);
        columnQueryWrapper.orderByAsc("column_position");
        List<MetadataColumnEntity> columnEntityList = metadataColumnDao.selectList(columnQueryWrapper);
        // 元数据（子表） TableStart:ColumnList TableEnd:ColumnList
        DataTable columnTable = new DataTable("ColumnList");
        columnTable.getColumns().add("id");
        columnTable.getColumns().add("tid");
        columnTable.getColumns().add("columnPosition");
        columnTable.getColumns().add("columnName");
        columnTable.getColumns().add("dataType");
        columnTable.getColumns().add("dataLength");
        columnTable.getColumns().add("dataPrecision");
        columnTable.getColumns().add("dataScale");
        columnTable.getColumns().add("columnNullable");
        columnTable.getColumns().add("columnKey");
        columnTable.getColumns().add("dataDefault");
        columnTable.getColumns().add("columnComment");
        for (MetadataColumnEntity column : columnEntityList) {
            DataRow row = columnTable.newRow();
            row.set(0, column.getId());
            row.set(1, column.getTableId());
            row.set(2, column.getColumnPosition());
            row.set(3, column.getColumnName());
            row.set(4, column.getDataType());
            row.set(5, column.getDataLength());
            row.set(6, column.getDataPrecision());
            row.set(7, column.getDataScale());
            row.set(8, "1".equals(column.getColumnNullable()) ? "Y" : "N");
            row.set(9, "1".equals(column.getColumnKey()) ? "Y" : "N");
            row.set(10, column.getDataDefault());
            row.set(11, column.getColumnComment());
            columnTable.getRows().add(row);
        }
        // 提供数据源
        DataSet dataSet = new DataSet();
        dataSet.getTables().add(tableTable);
        dataSet.getTables().add(columnTable);
        DataRelation dataRelation = new DataRelation("TableColumnRelation", tableTable.getColumns().get("id"), columnTable.getColumns().get("tid"));
        dataSet.getRelations().add(dataRelation);
        // 合并模版
        ClassPathResource classPathResource = new ClassPathResource("templates/metadata_1.0.0.doc");
        InputStream inputStream = classPathResource.getInputStream();
        Document doc = WordUtil.getInstance().getDocument(inputStream);
        // 提供数据源
        String[] fieldNames = new String[]{"database"};
        Object[] fieldValues = new Object[]{dbName.toUpperCase()};
        MailMerge mailMerge = doc.getMailMerge();
        mailMerge.execute(fieldNames, fieldValues);
        mailMerge.executeWithRegions(dataSet);
        WordUtil.getInstance().insertWatermarkText(doc, SecurityUtil.getUserName());
        return doc;
    }

    @Override
    public void refreshMetadata() {

    }
}
